/**
 * @author mmbro
 * @homepage https://gitee.com/mmbro/
 * @date 2021年05月18日02:07:16
 * @description 注释:加解密工具类
 * @depend JSEncrypt v3.2.0
 */
RsaUtil = class RsaUtil {
    /**
     * @description 内置公钥
     * @type {string}
     * @private
     * @link https://www.jb51.net/article/164815.htm (js class私有属性使用#号)
     */
    #_PUBLIC_KEY = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8HMr2CBpoZPm3t9tCVlrKtTmI4jNJc7/HhxjIEiDjC8czP4PV+44LjXvLYcSV0fwi6nE4LH2c5PBPEnPfqp0g8TZeX+bYGvd70cXee9d8wHgBqi4k0J0X33c0ZnW7JruftPyvJo9OelYSofBXQTcwI+3uIl/YvrgQRv6A5mW01QIDAQAB';
    /**
     * @description 内置私钥
     * @type {string}
     * @private
     */
    #_PRIVATE_KEY = 'MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALwcyvYIGmhk+be320JWWsq1OYjiM0lzv8eHGMgSIOMLxzM/g9X7jguNe8thxJXR/CLqcTgsfZzk8E8Sc9+qnSDxNl5f5tga93vRxd5713zAeAGqLiTQnRffdzRmdbsmu5+0/K8mj056VhKh8FdBNzAj7e4iX9i+uBBG/oDmZbTVAgMBAAECgYEAmgNU5NTDkj9B+Pnt6UU8doSjw3+3j+bV2K2yS3QUOvAUus/Ax7x6ktjWxzCXvDY9IfUil2RNv9vtKEAqYLCWjc+lf8PV/yH1b7NEgyeAPBXtAJRoOnmYL2bdPW92kP9KgxJruF6Dz/C5AmMOncsvq8ABD+9Darn4p8dwj2ZC4O0CQQDf/AHmZsQokEItfCy4mHS9UbxbfIhEUv1ApPh/+Sr7NkJkHWYCtBQo+8jKO6zurAZQgWBPD1XX2UE4R+VIiZazAkEA1wAqtMvGhccyRZr+6kpkpDIa8+9jOE+nGUzqTDvgCID6as8AzOONFVVK6m/UUqkhcJ8Qu1pF36BGojy5BX2KVwJBAJSFpbji0hXXupowqfLp3RcgmNbNWAp+QUJZYhJx5cdYbmO2fssyH+AhPT6knYJR/YnqkDM8hv6vKCkqu2YDHjMCQAOA8TE5EOclM+CGghj3VWSHnIDVKdzFD4gOBNNxNlltIKeU8AJmwunSFgJ0CBXAw9a+ANvMwM7AIeaK7sj0HskCQAvxfDCq7gaNx+pfu0FHG8Gix08A/A6foggBl1fVu+L9sr9ZuOQ3HbXnl28F9ewuB9xdjnLUDjp7W7U0pB+vKoQ=';
    #encrypt = new JSEncrypt();
    static #rsa = new RsaUtil();
    static __init__ = true;

    /**
     * @description 构造方法,指定公钥与私钥(如未传则使用内置私钥与公钥)
     * @param publicKey rsa公钥
     * @param privateKey rsa私钥
     */
    constructor(publicKey, privateKey) {
        if (!!RsaUtil.__init__) {
            console.log("do not init this twice!");
            return;
        } else {
            RsaUtil.__init__ = true;
        }
        console.log('RsaUtil.__init__', RsaUtil.__init__);
        if (!!publicKey && !!privateKey) {
            this.#_PUBLIC_KEY = publicKey;
            this.#_PRIVATE_KEY = privateKey;
        }
        this.#encrypt = new JSEncrypt();
        this.#encrypt.setPublicKey('-----BEGIN PUBLIC KEY-----' + this.#_PUBLIC_KEY + '-----END PUBLIC KEY-----');
        this.#encrypt.setPrivateKey('-----BEGIN RSA PRIVATE KEY-----' + this.#_PRIVATE_KEY + '-----END RSA PRIVATE KEY-----');
    }

    static rsaEncrypt(str, encrypt) {
        if (!encrypt) {
            encrypt = RsaUtil.#rsa.#encrypt;
        }
        str = String(str);
        console.log(encrypt);
        return encrypt.encrypt(str);
    }

    static rsaDecrypt(str, encrypt) {
        if (!encrypt) {
            encrypt = RsaUtil.#rsa.#encrypt;
        }
        if (str === null || String(str).length === 0) {
            return '';
        }
        return encrypt.decrypt(str);
    }

    /**
     * @description 生成一副rsa秘钥对
     * @param callback{function(Object)} 回调函数,{publicKey extends string,privateKey extends string}
     */
    static genRsaKeyPair(callback) {
        if (typeof callback !== 'function') {
            throw 'callback must be a function!';
        }
        RsaUtil.#rsa.#getRsaKeys(callback);
    }

    /**
     * @description 获取密钥对,返回公钥和私钥的json对象
     * @link https://blog.csdn.net/weixin_41612889/article/details/100026392
     * @param callback{function(Object)} 回调函数,{publicKey extends string,privateKey extends string}
     */
    #getRsaKeys(callback) {
        let _RSA2text = RsaUtil.#RSA2text;
        crypto.subtle.generateKey(
            {
                name: "RSA-OAEP",
                modulusLength: 2048, //can be 1024, 2048, or 4096
                publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
                hash: {name: "SHA-512"}, //can be "SHA-1", "SHA-256", "SHA-384", or "SHA-512"
            },
            true, //whether the key is extractable (i.e. can be used in exportKey)
            ["encrypt", "decrypt"] //must be ["encrypt", "decrypt"] or ["wrapKey", "unwrapKey"]
        ).then(function (key) {
            crypto.subtle.exportKey(
                "pkcs8",
                key.privateKey
            ).then(function (keyData1) {
                crypto.subtle.exportKey(
                    "spki",
                    key.publicKey
                ).then(function (keyData2) {
                    let privateKey = _RSA2text(keyData1, 1);
                    let publicKey = _RSA2text(keyData2);
                    let keyPair = {publicKey, privateKey};
                    callback(keyPair);
                }).catch(function (err) {
                    console.error(err);
                });
            })
                .catch(function (err) {
                    console.error(err);
                });
        })
            .catch(function (err) {
                console.error(err);
            });
    }

    static #RSA2text(buffer, isPrivate = 0) {
        let binary = '';
        let bytes = new Uint8Array(buffer);
        let len = bytes.byteLength;
        for (let i = 0; i < len; i++) {
            binary += String.fromCharCode(bytes[i]);
        }
        let base64 = btoa(binary);
        let text = "-----BEGIN " + (isPrivate ? "PRIVATE" : "PUBLIC") + " KEY-----\n";
        text += base64.replace(/[^\x00-\xff]/g, "$&\x01").replace(/.{64}\x01?/g, "$&\n");
        text += "\n-----END " + (isPrivate ? "PRIVATE" : "PUBLIC") + " KEY-----";
        return text;
    }

    /**
     * @description 公钥只可更新,无法获取
     * @param value 公钥
     * @constructor
     */
    static set PUBLIC_KEY(value) {
        console.log('set PUBLIC_KEY', value)
        RsaUtil.#rsa.#_PUBLIC_KEY = value;
        RsaUtil.#rsa.#encrypt.setPublicKey(value);
    }

    /**
     * @description 私钥只可更新,无法获取
     * @param value 私钥
     * @constructor
     */
    static set PRIVATE_KEY(value) {
        console.log('set PRIVATE_KEY', value)
        RsaUtil.#rsa.#_PRIVATE_KEY = value;
        RsaUtil.#rsa.#encrypt.setPrivateKey(value);
    }
}